home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / DB / ldap.php < prev    next >
PHP Script  |  2004-10-01  |  35KB  |  999 lines

  1. <?php
  2. //
  3. // Pear DB LDAP - Database independent query interface definition
  4. // for PHP's LDAP extension.
  5. //
  6. // Copyright (c) 2002-2003 Ludovico Magnocavallo <ludo@sumatrasolutions.com>
  7. //
  8. //  This library is free software; you can redistribute it and/or
  9. //  modify it under the terms of the GNU Lesser General Public
  10. //  License as published by the Free Software Foundation; either
  11. //  version 2.1 of the License, or (at your option) any later version.
  12. //
  13. //  This library is distributed in the hope that it will be useful,
  14. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16. //  Lesser General Public License for more details.
  17. //
  18. //  You should have received a copy of the GNU Lesser General Public
  19. //  License along with this library; if not, write to the Free Software
  20. //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  21. //
  22. // Contributors
  23. // - Piotr Roszatycki <dexter@debian.org>
  24. //   DB_ldap::base() method, support for LDAP sequences, various fixes
  25. // - Aaron Spencer Hawley <aaron dot hawley at uvm dot edu>
  26. //   fix to use port number if present in DB_ldap->connect()
  27. //
  28. // $Id: ldap.php,v 1.21 2004/05/20 21:52:05 ludoo Exp $
  29. //
  30.  
  31. require_once 'DB/common.php';
  32.  
  33. define("DB_ERROR_BIND_FAILED",     -26);
  34. define("DB_ERROR_UNKNOWN_LDAP_ACTION",     -27);
  35.  
  36. /**
  37.  * LDAP result class
  38.  *
  39.  * LDAP_result extends DB_result to provide specific LDAP
  40.  * result methods.
  41.  *
  42.  * @version 1.0
  43.  * @author Ludovico Magnocavallo <ludo@sumatrasolutions.com>
  44.  * @package DB
  45.  */
  46.  
  47. class LDAP_result extends DB_result
  48. {
  49.  
  50.     // {{{ properties
  51.  
  52.     /**
  53.      * data returned from ldap_entries()
  54.      * @access private
  55.      */
  56.     var $_entries   = null;
  57.     /**
  58.      * result rows as hash of records
  59.      * @access private
  60.      */
  61.     var $_recordset = null;
  62.     /**
  63.      * current record as hash
  64.      * @access private
  65.      */
  66.     var $_record    = null;
  67.  
  68.     // }}}
  69.     // {{{ constructor
  70.  
  71.     /**
  72.      * class constructor, calls DB_result constructor
  73.      * @param ref $dbh reference to the db instance
  74.      * @param resource $result ldap command result
  75.      */
  76.     function LDAP_result(&$dbh, $result)
  77.     {
  78.         $this->DB_result($dbh, $result);
  79.     }
  80.  
  81.     /**
  82.      * fetch rows of data into $this->_recordset
  83.      *
  84.      * called once as soon as something needs to be returned
  85.      * @access private
  86.      * @param resource $result ldap command result
  87.      * @return boolean true
  88.      */
  89.     function getRows() {
  90.         if ($this->_recordset === null) {
  91.             // begin processing result into recordset
  92.             $this->_entries = ldap_get_entries($this->dbh->connection, $this->result);
  93.             $this->row_counter = $this->_entries['count'];
  94.             $i = 1;
  95.             $rs_template = array();
  96.             if (count($this->dbh->attributes) > 0) {
  97.                 reset($this->dbh->attributes);
  98.                 while (list($a_index, $a_name) = each($this->dbh->attributes)) $rs_template[$a_name] = '';
  99.             }
  100.             while (list($entry_idx, $entry) = each($this->_entries)) {
  101.                 // begin first loop, iterate through entries
  102.                 if (!empty($this->dbh->limit_from) && ($i < $this->dbh->limit_from)) continue;
  103.                 if (!empty($this->dbh->limit_count) && ($i > $this->dbh->limit_count)) break;
  104.                 $rs = $rs_template;
  105.                 if (!is_array($entry)) continue;
  106.                 while (list($attr, $attr_values) = each($entry)) {
  107.                     // begin second loop, iterate through attributes
  108.                     if (is_int($attr) || $attr == 'count') continue;
  109.                     if (is_string($attr_values)) $rs[$attr] = $attr_values;
  110.                     else {
  111.                         $value = '';
  112.                         while (list($value_idx, $attr_value) = each($attr_values)) {
  113.                             // begin third loop, iterate through attribute values
  114.                             if (!is_int($value_idx)) continue;
  115.                             if (empty($value)) $value = $attr_value;
  116.                             else {
  117.                                 if (is_array($value)) $value[] = $attr_value;
  118.                                 else $value = array($value, $attr_value);
  119.                             }
  120. //                          else $value .= "\n$attr_value";
  121.                             // end third loop
  122.                         }
  123.                         $rs[$attr] = $value;
  124.                     }
  125.                     // end second loop
  126.                 }
  127.                 reset($rs);
  128.                 $this->_recordset[$entry_idx] = $rs;
  129.                 $i++;
  130.                 // end first loop
  131.             }
  132.             $this->_entries = null;
  133.             if (!is_array($this->_recordset))
  134.                 $this->_recordset = array();
  135.             if (!empty($this->dbh->sorting)) {
  136.                 $sorting_method = (!empty($this->dbh->sorting_method) ? $this->dbh->sorting_method : 'cmp');
  137.                 uksort($this->_recordset, array(&$this, $sorting_method));
  138.             }
  139.             reset($this->_recordset);
  140.             // end processing result into recordset
  141.         }
  142.         return DB_OK;
  143.     }
  144.  
  145.  
  146.     /**
  147.      * Fetch and return a row of data (it uses driver->fetchInto for that)
  148.      * @param int $fetchmode  format of fetched row
  149.      * @param int $rownum     the row number to fetch
  150.      *
  151.      * @return  array a row of data, NULL on no more rows or PEAR_Error on error
  152.      *
  153.      * @access public
  154.      */
  155.     function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
  156.     {
  157.         $this->getRows();
  158.         if (count($this->_recordset) == 0) return null;
  159.         if ($this->_record !== null) $this->_record = next($this->_recordset);
  160.         else $this->_record = current($this->_recordset);
  161.         $row = $this->_record;
  162.         return $row;
  163.     }
  164.  
  165.  
  166.     /**
  167.      * Fetch a row of data into an existing variable.
  168.      *
  169.      * @param  mixed     $arr        reference to data containing the row
  170.      * @param  integer   $fetchmode  format of fetched row
  171.      * @param  integer   $rownum     the row number to fetch
  172.      *
  173.      * @return  mixed  DB_OK on success, NULL on no more rows or
  174.      *                 a DB_Error object on error
  175.      *
  176.      * @access public
  177.      */
  178.  
  179.     function fetchInto(&$ar, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
  180.     {
  181.         $this->getRows();
  182.         if ($this->_record !== null) $this->_record = next($this->_recordset);
  183.         else $this->_record = current($this->_recordset);
  184.         $ar = $this->_record;
  185.         if (!$ar) {
  186.             return null;
  187.         }
  188.         return DB_OK;
  189.     }
  190.  
  191.     /**
  192.      * return all records
  193.      *
  194.      * returns a hash of all records, basically returning
  195.      * a copy of $this->_recordset
  196.      * @param  integer   $fetchmode  format of fetched row
  197.      * @param  integer   $rownum     the row number to fetch (not used, here for interface compatibility)
  198.      *
  199.      * @return  mixed  DB_OK on success, NULL on no more rows or
  200.      *                 a DB_Error object on error
  201.      *
  202.      * @access public
  203.      */
  204.     function fetchAll($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null)
  205.     {
  206.         $this->getRows();
  207.         return($this->_recordset);
  208.     }
  209.  
  210.     /**
  211.      * Get the the number of columns in a result set.
  212.      *
  213.      * @return int the number of columns, or a DB error
  214.      *
  215.      * @access public
  216.      */
  217.     function numCols($result)
  218.     {
  219.         $this->getRows();
  220.         return(count(array_keys($this->_record)));
  221.     }
  222.  
  223.     function cmp($a, $b)
  224.     {
  225.         return(strcmp(strtolower($this->_recordset[$a][$this->dbh->sorting]), strtolower($this->_recordset[$b][$this->dbh->sorting])));
  226.     }
  227.  
  228.     /**
  229.      * Get the number of rows in a result set.
  230.      *
  231.      * @return int the number of rows, or a DB error
  232.      *
  233.      * @access public
  234.      */
  235.     function numRows()
  236.     {
  237.         $this->getRows();
  238.         return $this->row_counter;
  239.     }
  240.  
  241.     /**
  242.      * Get the next result if a batch of queries was executed.
  243.      *
  244.      * @return bool true if a new result is available or false if not.
  245.      *
  246.      * @access public
  247.      */
  248.     function nextResult()
  249.     {
  250.         return $this->dbh->nextResult($this->result);
  251.     }
  252.  
  253.     /**
  254.      * Frees the resources allocated for this result set.
  255.      * @return  int     error code
  256.      *
  257.      * @access public
  258.      */
  259.     function free()
  260.     {
  261.         $this->_recordset = null;
  262.         $this->_record = null;
  263.         ldap_free_result($this->result);
  264.         $this->result = null;
  265.         return true;
  266.     }
  267.  
  268.     /**
  269.     * @deprecated
  270.     */
  271.     function tableInfo($mode = null)
  272.     {
  273.         return $this->dbh->tableInfo($this->result, $mode);
  274.     }
  275.  
  276.     /**
  277.     * returns the actual rows number
  278.     * @return integer
  279.     */
  280.     function getRowCounter()
  281.     {
  282.         $this->getRows();
  283.         return $this->row_counter;
  284.     }
  285. }
  286.  
  287. /**
  288.  * LDAP DB interface class
  289.  *
  290.  * LDAP extends DB_common to provide DB compliant
  291.  * access to LDAP servers
  292.  *
  293.  * @version 1.0
  294.  * @author Ludovico Magnocavallo <ludo@sumatrasolutions.com>
  295.  * @package DB
  296.  */
  297.  
  298. class DB_ldap extends DB_common
  299. {
  300.     // {{{ properties
  301.  
  302.     /**
  303.      * LDAP connection
  304.      * @access private
  305.      */
  306.     var $connection;
  307.     /**
  308.      * base dn
  309.      * @access private
  310.      */
  311.     var $base           = '';
  312.     /**
  313.      * default base dn
  314.      * @access private
  315.      */
  316.     var $d_base           = '';
  317.     /**
  318.      * query base dn
  319.      * @access private
  320.      */
  321.     var $q_base           = '';
  322.     /**
  323.      * array of LDAP actions that only manipulate data
  324.      * returning a true/false value
  325.      * @access private
  326.      */
  327.     var $manip          = array('add', 'compare', 'delete', 'modify', 'mod_add', 'mod_del', 'mod_replace', 'rename');
  328.     /**
  329.      * store the default real LDAP action to perform
  330.      * @access private
  331.      */
  332.     var $action         = 'search';
  333.     /**
  334.      * store the real LDAP action to perform
  335.      * (ie PHP ldap function to call) for a query
  336.      * @access private
  337.      */
  338.     var $q_action       = '';
  339.     /**
  340.      * store optional parameters passed
  341.      *  to the real LDAP action
  342.      * @access private
  343.      */
  344.     var $q_params       = array();
  345.  
  346.     // }}}
  347.  
  348.     /**
  349.      * Constructor, calls DB_common constructor
  350.      *
  351.      * @see DB_common::DB_common()
  352.      */
  353.     function DB_ldap()
  354.     {
  355.         $this->DB_common();
  356.         $this->phptype = 'ldap';
  357.         $this->dbsyntax = 'ldap';
  358.         $this->features = array(
  359.             'prepare'       => false,
  360.             'pconnect'      => false,
  361.             'transactions'  => false,
  362.             'limit'         => false
  363.         );
  364.         $this->errorcode_map = array(
  365.             0x10 => DB_ERROR_NOSUCHFIELD,               // LDAP_NO_SUCH_ATTRIBUTE
  366.             0x11 => DB_ERROR_INVALID,                   // LDAP_UNDEFINED_TYPE
  367.             0x12 => DB_ERROR_INVALID,                   // LDAP_INAPPROPRIATE_MATCHING
  368.             0x13 => DB_ERROR_INVALID,                   // LDAP_CONSTRAINT_VIOLATION
  369.             0x14 => DB_ERROR_ALREADY_EXISTS,            // LDAP_TYPE_OR_VALUE_EXISTS
  370.             0x15 => DB_ERROR_INVALID,                   // LDAP_INVALID_SYNTAX
  371.             0x20 => DB_ERROR_NOT_FOUND,                 // LDAP_NO_SUCH_OBJECT
  372.             0x21 => DB_ERROR_NOT_FOUND,                 // LDAP_ALIAS_PROBLEM
  373.             0x22 => DB_ERROR_INVALID,                   // LDAP_INVALID_DN_SYNTAX
  374.             0x23 => DB_ERROR_INVALID,                   // LDAP_IS_LEAF
  375.             0x24 => DB_ERROR_INVALID,                   // LDAP_ALIAS_DEREF_PROBLEM
  376.             0x30 => DB_ERROR_ACCESS_VIOLATION,          // LDAP_INAPPROPRIATE_AUTH
  377.             0x31 => DB_ERROR_ACCESS_VIOLATION,          // LDAP_INVALID_CREDENTIALS
  378.             0x32 => DB_ERROR_ACCESS_VIOLATION,          // LDAP_INSUFFICIENT_ACCESS
  379.             0x40 => DB_ERROR_MISMATCH,                  // LDAP_NAMING_VIOLATION
  380.             0x41 => DB_ERROR_MISMATCH,                  // LDAP_OBJECT_CLASS_VIOLATION
  381.             0x44 => DB_ERROR_ALREADY_EXISTS,            // LDAP_ALREADY_EXISTS
  382.             0x51 => DB_ERROR_CONNECT_FAILED,            // LDAP_SERVER_DOWN
  383.             0x57 => DB_ERROR_SYNTAX                     // LDAP_FILTER_ERROR
  384.         );
  385.     }
  386.  
  387.     /**
  388.      * Connect and bind to LDAP server with either anonymous or authenticated bind depending on dsn info
  389.      *
  390.      * @param array $dsninfo dsn info as passed by DB::connect()
  391.      * @param boolean $persistent kept for interface compatibility
  392.      * @return DB_OK if successfully connected. A DB error code is returned on failure.
  393.      */
  394.     function connect($dsninfo, $persistent = false)
  395.     {
  396.         if (!DB::assertExtension('ldap'))
  397.             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  398.  
  399.         $this->dsn = $dsninfo;
  400.         $user   = $dsninfo['username'];
  401.         $pw     = $dsninfo['password'];
  402.         if (($colon_pos = strpos($dsninfo['hostspec'], ':')) !== false) {
  403.             $host = substr($dsninfo['hostspec'], 0, $colon_pos);
  404.             $port = substr($dsninfo['hostspec'], $colon_pos + 1);
  405.         } else {
  406.             $host = $dsninfo['hostspec'];
  407.             $port = null;
  408.         }
  409.         $this->base = $dsninfo['database'];
  410.         $this->d_base = $this->base;
  411.  
  412.         if (empty($host)) {
  413.             return $this->raiseError("no host specified $host");
  414.         } // else ...
  415.  
  416.         if (isset($port)) {
  417.             $conn = ldap_connect($host, $port);
  418.         } else {
  419.             $conn = ldap_connect($host);
  420.         }
  421.         if (!$conn) {
  422.             return $this->raiseError(DB_ERROR_CONNECT_FAILED);
  423.         }
  424.         if ($user && $pw) {
  425.             $bind = @ldap_bind($conn, $user, $pw);
  426.         } else {
  427.             $bind = @ldap_bind($conn);
  428.         }
  429.         if (!$bind) {
  430.             return $this->raiseError(DB_ERROR_BIND_FAILED);
  431.         }
  432.         $this->connection = $conn;
  433.         return DB_OK;
  434.     }
  435.  
  436.     /**
  437.      * Unbinds from LDAP server
  438.      *
  439.      * @return int ldap_unbind() return value
  440.      */
  441.     function disconnect()
  442.     {
  443.         $ret = @ldap_unbind($this->connection);
  444.         $this->connection = null;
  445.         return $ret;
  446.     }
  447.  
  448.  
  449.     /**
  450.      * Performs a request against the LDAP server
  451.      *
  452.      * The type of request (and the corresponding PHP ldap function called)
  453.      * depend on two additional parameters, added in respect to the
  454.      * DB_common interface.
  455.      *
  456.      * @param string $filter text of the request to send to the LDAP server
  457.      * @param string $action type of request to perform, defaults to search (ldap_search())
  458.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  459.      * @return result from ldap function or DB Error object if no result
  460.      */
  461.     function simpleQuery($filter, $action = null, $params = null)
  462.     {
  463.         if ($action === null) {
  464.             $action = (!empty($this->q_action) ? $this->q_action : $this->action);
  465.         }
  466.         if ($params === null) {
  467.             $params = (count($this->q_params) > 0 ? $this->q_params : array());
  468.         }
  469.         if (!$this->isManip($action)) {
  470.             $base = $this->q_base ? $this->q_base : $this->base;
  471.             $attributes = array();
  472.             $attrsonly = 0;
  473.             $sizelimit = 0;
  474.             $timelimit = 0;
  475.             $deref = LDAP_DEREF_NEVER;
  476.             $sorting = '';
  477.             $sorting_method = '';
  478.             reset($params);
  479.             while (list($k, $v) = each($params)) {
  480.                 if (isset(${$k})) ${$k} = $v;
  481.             }
  482.             $this->sorting = $sorting;
  483.             $this->sorting_method = $sorting_method;
  484.             $this->attributes = $attributes;
  485.             # double escape char for filter: '(o=Przedsi\C4\99biorstwo)' => '(o=Przedsi\\C4\\99biorstwo)'
  486.             $filter = str_replace('\\', '\\\\', $filter);
  487.             $this->last_query = $filter;
  488.             if ($action == 'search')
  489.                 $result = @ldap_search($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref);
  490.             else if ($action == 'list')
  491.                 $result = @ldap_list($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref);
  492.             else if ($action == 'read')
  493.                 $result = @ldap_read($this->connection, $base, $filter, $attributes, $attrsonly, $sizelimit, $timelimit, $deref);
  494.             else
  495.                 return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION);
  496.             if (!$result) {
  497.                 return $this->ldapRaiseError();
  498.             }
  499.         } else {
  500.             # If first argument is an array, it contains the entry with DN.
  501.             if (is_array($filter)) {
  502.                 $entry = $filter;
  503.                 $filter = $entry["dn"];
  504.             } else {
  505.                 $entry = array();
  506.             }
  507.             unset($entry["dn"]);
  508.             $attribute      = '';
  509.             $value          = '';
  510.             $newrdn         = '';
  511.             $newparent      = '';
  512.             $deleteoldrdn   = false;
  513.             reset($params);
  514.             while (list($k, $v) = each($params)) {
  515.                 if (isset(${$k})) ${$k} = $v;
  516.             }
  517.             $this->last_query = $filter;
  518.             if ($action == 'add')
  519.                 $result = @ldap_add($this->connection, $filter, $entry);
  520.             else if ($action == 'compare')
  521.                 $result = @ldap_add($this->connection, $filter, $attribute, $value);
  522.             else if ($action == 'delete')
  523.                 $result = @ldap_delete($this->connection, $filter);
  524.             else if ($action == 'modify')
  525.                 $result = @ldap_modify($this->connection, $filter, $entry);
  526.             else if ($action == 'mod_add')
  527.                 $result = @ldap_mod_add($this->connection, $filter, $entry);
  528.             else if ($action == 'mod_del')
  529.                 $result = @ldap_mod_del($this->connection, $filter, $entry);
  530.             else if ($action == 'mod_replace')
  531.                 $result = @ldap_mod_replace($this->connection, $filter, $entry);
  532.             else if ($action == 'rename')
  533.                 $result = @ldap_rename($this->connection, $filter, $newrdn, $newparent, $deleteoldrdn);
  534.             else
  535.                 return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION);
  536.             if (!$result) {
  537.                 return $this->ldapRaiseError();
  538.             }
  539.         }
  540.         $this->freeQuery();
  541.         return $result;
  542.     }
  543.  
  544.     /**
  545.      * Executes a query performing variables substitution in the query text
  546.      *
  547.      * @param string $stmt text of the request to send to the LDAP server
  548.      * @param array $data query variables values to substitute
  549.      * @param string $action type of request to perform, defaults to search (ldap_search())
  550.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  551.      * @return LDAP_result object or DB Error object if no result
  552.      * @see DB_common::executeEmulateQuery $this->simpleQuery()
  553.      */
  554.     function execute($stmt, $data = false, $action = null, $params = array())
  555.     {
  556.         $this->q_params = $params;
  557.         $realquery = $this->executeEmulateQuery($stmt, $data);
  558.         if (DB::isError($realquery)) {
  559.             return $realquery;
  560.         }
  561.         $result = $this->simpleQuery($realquery);
  562.         if (DB::isError($result) || $result === DB_OK) {
  563.             return $result;
  564.         } else {
  565.             return new LDAP_result($this, $result);
  566.         }
  567.     }
  568.  
  569.     /**
  570.      * Executes multiple queries performing variables substitution for each query
  571.      *
  572.      * @param string $stmt text of the request to send to the LDAP server
  573.      * @param array $data query variables values to substitute
  574.      * @param string $action type of request to perform, defaults to search (ldap_search())
  575.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  576.      * @return LDAP_result object or DB Error object if no result
  577.      * @see DB_common::executeMultiple
  578.      */
  579.     function executeMultiple($stmt, &$data, $action = null, $params = array())
  580.     {
  581.         $this->q_action = $action ? $action : $this->action;
  582.         $this->q_params = $params;
  583.         return(parent::executeMultiple($stmt, $data));
  584.     }
  585.  
  586.     /**
  587.      * Executes a query substituting variables if any are present
  588.      *
  589.      * @param string $query text of the request to send to the LDAP server
  590.      * @param array $data query variables values to substitute
  591.      * @param string $action type of request to perform, defaults to search (ldap_search())
  592.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  593.      * @return LDAP_result object or DB Error object if no result
  594.      * @see DB_common::prepare() $this->execute()$this->simpleQuery()
  595.      */
  596.     function &query($query, $data = array(), $action = null, $params = array()) {
  597.         // $this->q_action = $action ? $action : $this->action;
  598.         // $this->q_params = $params;
  599.         if (sizeof($data) > 0) {
  600.             $sth = $this->prepare($query);
  601.             if (DB::isError($sth)) {
  602.                 return $sth;
  603.             }
  604.             return $this->execute($sth, $data);
  605.         } else {
  606.             $result = $this->simpleQuery($query);
  607.             if (DB::isError($result) || $result === DB_OK) {
  608.                 return $result;
  609.             } else {
  610.                 return new LDAP_result($this, $result);
  611.             }
  612.         }
  613.     }
  614.  
  615.     /**
  616.      * Modifies a query to return only a set of rows, stores $from and $count for LDAP_result
  617.      *
  618.      * @param string $query text of the request to send to the LDAP server
  619.      * @param int $from record position from which to start returning data
  620.      * @param int $count number of records to return
  621.      * @return modified query text (no modifications are made, see above)
  622.      */
  623.     function modifyLimitQuery($query, $from, $count)
  624.     {
  625.         $this->limit_from = $from;
  626.         $this->limit_count = $count;
  627.         return $query;
  628.     }
  629.  
  630.     /**
  631.      * Executes a query returning only a specified number of rows
  632.      *
  633.      * This method only saves the $from and $count parameters for LDAP_result
  634.      * where the actual records processing takes place
  635.      *
  636.      * @param string $query text of the request to send to the LDAP server
  637.      * @param int $from record position from which to start returning data
  638.      * @param int $count number of records to return
  639.      * @param string $action type of request to perform, defaults to search (ldap_search())
  640.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  641.      * @return LDAP_result object or DB Error object if no result
  642.      */
  643.     function limitQuery($query, $from, $count, $action = null, $params = array())
  644.     {
  645.         $query = $this->modifyLimitQuery($query, $from, $count);
  646.         $this->q_action = $action ? $action : $this->action;
  647.         $this->q_params = $params;
  648.         return $this->query($query, $action, $params);
  649.     }
  650.  
  651.     /**
  652.      * Fetch the first column of the first row of data returned from
  653.      * a query.  Takes care of doing the query and freeing the results
  654.      * when finished.
  655.      *
  656.      * @param $query the SQL query
  657.      * @param $data if supplied, prepare/execute will be used
  658.      *        with this array as execute parameters
  659.      * @param string $action type of request to perform, defaults to search (ldap_search())
  660.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  661.      * @return array
  662.      * @see DB_common::getOne()
  663.      * @access public
  664.      */
  665.     function &getOne($query, $data = array(), $action = null, $params = array())
  666.     {
  667.         $this->q_action = $action ? $action : $this->action;
  668.         $this->q_params = $params;
  669.         return(parent::getOne($query, $data));
  670.     }
  671.  
  672.     /**
  673.      * Fetch the first row of data returned from a query.  Takes care
  674.      * of doing the query and freeing the results when finished.
  675.      *
  676.      * @param $query the SQL query
  677.      * @param $fetchmode the fetch mode to use
  678.      * @param $data array if supplied, prepare/execute will be used
  679.      *        with this array as execute parameters
  680.      * @param string $action type of request to perform, defaults to search (ldap_search())
  681.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  682.      * @access public
  683.      * @return array the first row of results as an array indexed from
  684.      * 0, or a DB error code.
  685.      * @see DB_common::getRow()
  686.      * @access public
  687.      */
  688.     function &getRow($query,
  689.                      $data = null,
  690.                      $fetchmode = DB_FETCHMODE_DEFAULT,
  691.                      $action = null, $params = array())
  692.     {
  693.         $this->q_action = $action ? $action : $this->action;
  694.         $this->q_params = $params;
  695.         return(parent::getRow($query, $data, $fetchmode));
  696.     }
  697.  
  698.     /**
  699.      * Fetch the first column of data returned from a query.  Takes care
  700.      * of doing the query and freeing the results when finished.
  701.      *
  702.      * @param $query the SQL query
  703.      * @param $col which column to return (integer [column number,
  704.      * starting at 0] or string [column name])
  705.      * @param $data array if supplied, prepare/execute will be used
  706.      *        with this array as execute parameters
  707.      * @param string $action type of request to perform, defaults to search (ldap_search())
  708.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  709.      * @access public
  710.      * @return array an indexed array with the data from the first
  711.      * row at index 0, or a DB error code.
  712.      * @see DB_common::getCol()
  713.      * @access public
  714.      */
  715.     function &getCol($query, $col = 0, $data = array(), $action = null, $params = array())
  716.     {
  717.         $this->q_action = $action ? $action : $this->action;
  718.         $this->q_params = $params;
  719.         return(parent::getCol($query, $col, $data));
  720.     }
  721.  
  722.     /**
  723.      * Calls DB_common::getAssoc()
  724.      *
  725.      * @param $query the SQL query
  726.      * @param $force_array (optional) used only when the query returns
  727.      * exactly two columns.  If true, the values of the returned array
  728.      * will be one-element arrays instead of scalars.
  729.      * starting at 0] or string [column name])
  730.      * @param array $data if supplied, prepare/execute will be used
  731.      *        with this array as execute parameters
  732.      * @param $fetchmode the fetch mode to use
  733.      * @param boolean $group see DB_Common::getAssoc()
  734.      * @param string $action type of request to perform, defaults to search (ldap_search())
  735.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  736.      * @access public
  737.      * @return array an indexed array with the data from the first
  738.      * row at index 0, or a DB error code.
  739.      * @see DB_common::getAssoc()
  740.      * @access public
  741.      */
  742.     function &getAssoc($query, $force_array = false, $data = array(),
  743.                        $fetchmode = DB_FETCHMODE_ORDERED, $group = false,
  744.                        $action = null, $params = array())
  745.     {
  746.         $this->q_action = $action ? $action : $this->action;
  747.         $this->q_params = $params;
  748.         return(parent::getAssoc($query, $force_array, $data, $fetchmode, $group));
  749.     }
  750.  
  751.     /**
  752.      * Fetch all the rows returned from a query.
  753.      *
  754.      * @param $query the SQL query
  755.      * @param array $data if supplied, prepare/execute will be used
  756.      *        with this array as execute parameters
  757.      * @param $fetchmode the fetch mode to use
  758.      * @param string $action type of request to perform, defaults to search (ldap_search())
  759.      * @param array $params array of additional parameters to pass to the PHP ldap function requested
  760.      * @access public
  761.      * @return array an nested array, or a DB error
  762.      * @see DB_common::getAll()
  763.      */
  764.     function &getAll($query,
  765.                      $data = null,
  766.                      $fetchmode = DB_FETCHMODE_DEFAULT,
  767.                      $action = null, $params = array())
  768.     {
  769.         $this->q_action = $action ? $action : $this->action;
  770.         $this->q_params = $params;
  771.         return(parent::getAll($query, $data, $fetchmode));
  772.     }
  773.  
  774.     function numRows($result)
  775.     {
  776.         return $result->numRows();
  777.     }
  778.  
  779.     function getTables()
  780.     {
  781.         return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE);
  782.     }
  783.  
  784.     function getListOf($type)
  785.     {
  786.         return $this->ldapRaiseError(DB_ERROR_NOT_CAPABLE);
  787.     }
  788.  
  789.     function isManip($action)
  790.     {
  791.         return(in_array($action, $this->manip));
  792.     }
  793.  
  794.     function freeResult()
  795.     {
  796.         return true;
  797.     }
  798.  
  799.     function freeQuery($query = '')
  800.     {
  801.         $this->q_action = '';
  802.         $this->q_base   = '';
  803.         $this->q_params = array();
  804.         $this->attributes = null;
  805.         $this->sorting = '';
  806.         return true;
  807.     }
  808.  
  809.     // Deprecated, will be removed in future releases.
  810.     function base($base = null)
  811.     {
  812.         $this->q_base = ($base !== null) ? $base : null;
  813.         return true;
  814.     }
  815.  
  816.     function ldapSetBase($base = null)
  817.     {
  818.         $this->base = ($base !== null) ? $base : $this->d_base;
  819.         $this->q_base = '';
  820.         return true;
  821.     }
  822.  
  823.     function ldapSetAction($action = 'search')
  824.     {
  825.         if ($action != 'search' && $action != 'list' && $action != 'read') {
  826.             return $this->ldapRaiseError(DB_ERROR_UNKNOWN_LDAP_ACTION);
  827.         }
  828.         $this->action = $action;
  829.         $this->q_action = '';
  830.         return true;
  831.     }
  832.  
  833.     /**
  834.      * Get the next value in a sequence.
  835.      *
  836.      * LDAP provides transactions for only one entry and we need to
  837.      * prevent race condition. If unique value before and after modify
  838.      * aren't equal then wait and try again.
  839.      *
  840.      * The name of sequence is LDAP DN of entry.
  841.      *
  842.      * @access public
  843.      * @param string $seq_name the DN of the sequence
  844.      * @param bool $ondemand whether to create the sequence on demand
  845.      * @return a sequence integer, or a DB error
  846.      */
  847.     function nextId($seq_name, $ondemand = true)
  848.     {
  849.         $repeat = 0;
  850.         do {
  851.             // Get the sequence entry
  852.             $this->base($seq_name);
  853.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  854.             $data = $this->getRow("objectClass=*");
  855.             $this->popErrorHandling();
  856.  
  857.             if (DB::isError($data)) {
  858.                 // DB_ldap doesn't use DB_ERROR_NOT_FOUND
  859.                 if ($ondemand && $repeat == 0
  860.                 && $data->getCode() == DB_ERROR) {
  861.                 // Try to create sequence and repeat
  862.                     $repeat = 1;
  863.                     $data = $this->createSequence($seq_name);
  864.                     if (DB::isError($data)) {
  865.                         return $this->ldapRaiseError($data);
  866.                     }
  867.                 } else {
  868.                     // Other error
  869.                     return $this->ldapRaiseError($data);
  870.                 }
  871.             } else {
  872.                 // Increment sequence value
  873.                 $data["cn"]++;
  874.                 // Unique identificator of transaction
  875.                 $seq_unique = mt_rand();
  876.                 $data["uid"] = $seq_unique;
  877.                 // Modify the LDAP entry
  878.                 $this->pushErrorHandling(PEAR_ERROR_RETURN);
  879.                 $data = $this->simpleQuery($data, 'modify');
  880.                 $this->popErrorHandling();
  881.                 if (DB::isError($data)) {
  882.                     return $this->ldapRaiseError($data);
  883.                 }
  884.                 // Get the entry and check if it contains our unique value
  885.                 $this->base($seq_name);
  886.                 $data = $this->getRow("objectClass=*");
  887.                 if (DB::isError($data)) {
  888.                     return $this->ldapRaiseError($data);
  889.                 }
  890.                 if ($data["uid"] != $seq_unique) {
  891.                     // It is not our entry. Wait a little time and repeat
  892.                     sleep(1);
  893.                     $repeat = 1;
  894.                 } else {
  895.                     $repeat = 0;
  896.                 }
  897.             }
  898.         } while ($repeat);
  899.  
  900.         if (DB::isError($data)) {
  901.             return $data;
  902.         }
  903.         return $data["cn"];
  904.     }
  905.  
  906.     /**
  907.      * Create the sequence
  908.      *
  909.      * The sequence entry is based on core schema with extensibleObject,
  910.      * so it should work with any LDAP server which doesn't check schema
  911.      * or supports extensibleObject object class.
  912.      *
  913.      * Sequence name have to be DN started with "sn=$seq_id,", i.e.:
  914.      *
  915.      * $seq_name = "sn=uidNumber,ou=sequences,dc=php,dc=net";
  916.      *
  917.      * dn: $seq_name
  918.      * objectClass: top
  919.      * objectClass: extensibleObject
  920.      * sn: $seq_id
  921.      * cn: $seq_value
  922.      * uid: $seq_uniq
  923.      *
  924.      * @param string $seq_name the DN of the sequence
  925.      * @return mixed DB_OK on success or DB error on error
  926.      * @access public
  927.      */
  928.     function createSequence($seq_name)
  929.     {
  930.         // Extract $seq_id from DN
  931.         ereg("^([^,]*),", $seq_name, $regs);
  932.         $seq_id = $regs[1];
  933.  
  934.         // Create the sequence entry
  935.         $data = array(
  936.             dn => $seq_name,
  937.             objectclass => array("top", "extensibleObject"),
  938.             sn => $seq_id,
  939.             cn => 0,
  940.             uid => 0
  941.         );
  942.  
  943.         // Add the LDAP entry
  944.         $this->pushErrorHandling(PEAR_ERROR_RETURN);
  945.         $data = $this->simpleQuery($data, 'add');
  946.         $this->popErrorHandling();
  947.         return $data;
  948.     }
  949.  
  950.     /**
  951.      * Drop a sequence
  952.      *
  953.      * @param string $seq_name the DN of the sequence
  954.      * @return mixed DB_OK on success or DB error on error
  955.      * @access public
  956.      */
  957.     function dropSequence($seq_name)
  958.     {
  959.         // Delete the sequence entry
  960.         $data = array(
  961.             dn => $seq_name,
  962.         );
  963.         $this->pushErrorHandling(PEAR_ERROR_RETURN);
  964.         $data = $this->simpleQuery($data, 'delete');
  965.         $this->popErrorHandling();
  966.         return $data;
  967.     }
  968.  
  969.     // {{{ ldapRaiseError()
  970.  
  971.     function ldapRaiseError($errno = null)
  972.     {
  973.         if ($errno === null) {
  974.             $errno = $this->errorCode(ldap_errno($this->connection));
  975.         }
  976.         if ($this->q_action !== null) {
  977.             return $this->raiseError($errno, null, null,
  978.                 sprintf('%s base="%s" filter="%s"',
  979.                     $this->q_action, $this->q_base, $this->last_query
  980.                 ),
  981.                 $errno == DB_ERROR_UNKNOWN_LDAP_ACTION ? null : @ldap_error($this->connection));
  982.         } else {
  983.             return $this->raiseError($errno, null, null, "???",
  984.                 @ldap_error($this->connection));
  985.         }
  986.     }
  987.  
  988.     // }}}
  989.  
  990. }
  991.  
  992. /*
  993.  * Local variables:
  994.  * tab-width: 4
  995.  * c-basic-offset: 4
  996.  * End:
  997.  */
  998. ?>
  999.